AVL树对二叉树的要求较高,为了维护查找的性能,所做的计算和操作比较复杂,维护成本比较大。为了简化维护的成本,同时又能保证查找的性能,提出了红黑树的概念。
红黑树(RBT)性质
- 每个结点都是有颜色的,红色或黑色
- 根结点的颜色是黑色的
- 所有的空结点都是黑色的
- 每个红色结点的孩子都是黑色的
- 红黑树上任意一点到所有空结点的所有路径上的黑色结点树是相同的
- 新插入的节点颜色是红色的
红黑树相关术语说明及图例
旋转操作
右旋结点r操作步骤如下:
- 将结点r的指向结点s的左引用,改成指向结点s的右子树
- 右旋结点r和结点s
- 将结点s的右子树的引用改成指向结点r
如图所示:
左旋结点r操作步骤如下:
- 将结点r指向结点s的右引用,改成指向结点s的左子树
- 左旋结点r和结点s
- 将结点s左子树的引用改成指向结点r
如图所示:
插入操作
插入情况需要分情况讨论:
插入情况一
空树到新增一个节点,直接插入节点然后变色,如图所示:
插入情况二
只有一个根节点的话,根结点是黑色的,那么直接插入新增结点,如图所示:
插入情况三
插入结点N处父亲结点P是红色的,叔叔结点U是红色的,结点N表示新插入的结点。需要再细分情况讨论进行讨论:此情况下红黑树的可能是一颗完整的红黑树,也可能是一颗红黑树的的一部分子树;
- 若是一棵完成的红黑树,需要将父结点P和叔叔结点U变色,如图所示:
-
若是一棵红黑树的子树,需要分三步调整操作:
- 第一步,先将插入结点处N的父结点P和叔叔结点U变成黑色
- 第二步,再将祖父结点G结点变成红色,这样做的目的是保证经过结点G的路径中黑色结点的数不变,因为当前我们看到的红黑树是一个大的红黑树的子树,所以要考虑到性质5不被破坏
- 此时如果整棵红黑树满足红黑树的性质,则停止红黑树的调整;如果不满足红黑树的性质,就需要分情况讨论如果出现的情况是当前的这种情况就需要重复第一步、第二步。如果不是,就需要看情况四和情况五进行处理。
如图所示:
上图节点中的1,2,3,4,5代表结点之下的子树或是NIL结点,上图中显示的是一个中间过渡状态。后续都是以类似形式表示子树
插入情况四
插入结点N处的父结点P为红色,叔叔结点U为黑色,且新插入的结点N是结点P的右孩子,且结点P是结点G的右孩子,需要进行如下调整操作:
- 第一步,左旋结点P
- 第二步,修改结点P、N的位置
如图所示:
插入情况五
插入结点N处的父结点P为红色,叔叔结点U为黑色,且新插入的结点N是结点P的左孩子,且结点P是结点G的左孩子,需要进行如下调整操作:
- 对结点G进行右旋
- 调整结点P和结点G的位置
- 互换结点P和结点G的颜色
如图所示:
示例
情况三中的第二种情况以及情况四、情况五相对来说比较抽象,下面我举个例子来帮助大家理解,情况三、情况四和情况五之间的关系,例子是从网上看到的红黑树相关例子,在此基础上进行的调整,如图所示:
如上图所示对应情况三,结点4表示新插入的结点N,结点5为父结点P,结点8为叔叔结点U,需要按照情况三子树情况进行调整:
在通过情况三的调整操作之后得到的红黑树的状态正好符合情况四的描述,如图所示:如上图所示,结点7表示新插入的结点N,结点2为父结点P,结点14为叔叔结点U,结点11为祖辈结点G,按照情况四对应的操作进行调整,如图所示:
经过情况四的调整操作以后得到的红黑树状态正好符合情况五的描述,如图所示:
如上图所示,结点2表示新插入的结点N,结点7为父结点P,结点14为叔叔结点U,结点11为祖辈结点G,按照情况五的操作进行调整,如图所示:
红黑树插入总结
通过示例我们可以看出,红黑树相关情况之间的关系,情况三的发生只是一个开始;根据情况三的调整,可能会产生情况四但不一定,也有可能是会差生情况三,也有可能是情况五,但是不论是什么情况,我们只要记住对应的状况描述及相关的调整,就可以对红黑树进行调整了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。